package com.xbongbong.saas.service.impl;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.i18n.util.I18nMessageUtil;
import com.xbongbong.paas.constant.MessageConstant;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.entity.PaasMenuEntity;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FlowStatusEnum;
import com.xbongbong.paas.enums.MenuTypeEnum;
import com.xbongbong.paas.help.CommonHelp;
import com.xbongbong.paas.help.menu.InitJxcMenuHelp;
import com.xbongbong.paas.model.PaasMenuModel;
import com.xbongbong.paas.pojo.AccordConfigMenuPojo;
import com.xbongbong.paas.pojo.dto.FormEntityAddDTO;
import com.xbongbong.paas.pojo.dto.SaasFormSaveDTO;
import com.xbongbong.paas.redis.PaasRedisHelper;
import com.xbongbong.paas.redis.RedisConstant;
import com.xbongbong.paas.redis.RedisPrefixConstant;
import com.xbongbong.paas.service.PaasFormService;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.pro.constant.BusinessConstant;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.enums.menu.enums.JXCMenuEnum;
import com.xbongbong.pro.flowbill.dto.FlowBillInsertDTO;
import com.xbongbong.pro.form.pojo.dto.FormAddBatchDTO;
import com.xbongbong.pro.form.pojo.vo.FormAddBatchVO;
import com.xbongbong.pro.menu.pojo.dto.MenuInitDTO;
import com.xbongbong.pro.workflow.enums.WorkflowOperateEnum;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.CostAdjustProductEntity;
import com.xbongbong.saas.domain.entity.InstockProductEntity;
import com.xbongbong.saas.domain.entity.OutstockProductEntity;
import com.xbongbong.saas.domain.entity.StockFlowBillEntity;
import com.xbongbong.saas.domain.entity.StockFlowBillUpdateEntity;
import com.xbongbong.saas.domain.entity.StockSurplusRecordEntity;
import com.xbongbong.saas.domain.entity.WarehouseOristockEntity;
import com.xbongbong.saas.domain.entity.WarehouseOristockProductEntity;
import com.xbongbong.saas.domain.entity.ext.CostAdjustEntityExt;
import com.xbongbong.saas.domain.entity.ext.InstockEntityExt;
import com.xbongbong.saas.domain.entity.ext.OutstockEntityExt;
import com.xbongbong.saas.domain.entity.ext.ProductEntityExt;
import com.xbongbong.saas.domain.entity.ext.WarehouseEntityExt;
import com.xbongbong.saas.enums.CompanyConfigEnum;
import com.xbongbong.saas.enums.InstockTypeEnum;
import com.xbongbong.saas.enums.OutstockTypeEnum;
import com.xbongbong.saas.enums.StockTypeEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.ContractOutstockEnum;
import com.xbongbong.saas.enums.business.CostAdjustEnum;
import com.xbongbong.saas.enums.business.InstockEnum;
import com.xbongbong.saas.enums.business.OutstockEnum;
import com.xbongbong.saas.enums.business.PurchaseInstockEnum;
import com.xbongbong.saas.enums.business.RefundInstockEnum;
import com.xbongbong.saas.enums.business.ReturnedPurchaseOutstockEnum;
import com.xbongbong.saas.enums.business.WorkOrderOutstockEnum;
import com.xbongbong.saas.help.workflow.StockFlowBillHelp;
import com.xbongbong.saas.model.CostAdjustModel;
import com.xbongbong.saas.model.CostAdjustProductModel;
import com.xbongbong.saas.model.InstockModel;
import com.xbongbong.saas.model.InstockProductModel;
import com.xbongbong.saas.model.OutstockModel;
import com.xbongbong.saas.model.OutstockProductModel;
import com.xbongbong.saas.model.ProductModel;
import com.xbongbong.saas.model.StockFlowBillModel;
import com.xbongbong.saas.model.StockSurplusRecordModel;
import com.xbongbong.saas.model.WarehouseModel;
import com.xbongbong.saas.model.WarehouseOristockModel;
import com.xbongbong.saas.model.WarehouseOristockProductModel;
import com.xbongbong.saas.service.StockFlowBillService;
import com.xbongbong.sys.domain.entity.CompanyConfigEntity;
import com.xbongbong.sys.model.CompanyConfigModel;
import com.xbongbong.workflow.domain.entity.WorkflowTaskEntity;
import com.xbongbong.workflow.domain.entity.WorkflowTransferDataEntity;
import com.xbongbong.workflow.model.WorkflowTaskModel;
import com.xbongbong.workflow.model.WorkflowTransferDataModel;
import com.xbongbong.workflow.pojo.WorkflowTransferPOJO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Service("stockFlowBillService")
public class StockFlowBillServiceImpl implements StockFlowBillService {

    private static final Logger LOG = LoggerFactory.getLogger(StockFlowBillServiceImpl.class);

    @Resource
    private StockFlowBillModel stockFlowBillModel;
    @Resource
    private StockSurplusRecordModel stockSurplusRecordModel;
    @Resource
    private PaasMenuModel paasMenuModel;
    @Resource
    private InitJxcMenuHelp initJxcMenuHelp;
    @Resource
    private PaasFormService paasFormService;
    @Resource
    private WarehouseModel warehouseModel;
    @Resource
    private ProductModel productModel;
    @Resource
    private InstockModel instockModel;
    @Resource
    private InstockProductModel instockProductModel;
    @Resource
    private OutstockModel outstockModel;
    @Resource
    private OutstockProductModel outstockProductModel;
    @Resource
    private WarehouseOristockModel warehouseOristockModel;
    @Resource
    private WarehouseOristockProductModel warehouseOristockProductModel;
    @Resource
    private PaasRedisHelper paasRedisHelper;
    @Resource
    private CompanyConfigModel companyConfigModel;
    @Resource
    private StockFlowBillHelp stockFlowBillHelp;
    @Resource
    private CostAdjustProductModel costAdjustProductModel;
    @Resource
    private CostAdjustModel costAdjustModel;
    @Resource
    private WorkflowTransferDataModel workflowTransferDataModel;
    @Resource
    private WorkflowTaskModel workflowTaskModel;
    @Resource
    private CommonHelp commonHelp;


    @Async(value = "flowThreadPool")
    @Override
    public void caculateStockModification4Save(SaasFormSaveDTO saasFormSaveDTO) throws XbbException {
        stockFlowBillHelp.caculateStockModification4Save(saasFormSaveDTO.getCorpid(), saasFormSaveDTO.getBusinessType(), saasFormSaveDTO.getNewData(), saasFormSaveDTO.getNewPaasFormDataEntity());
    }

    @Async(value = "flowThreadPool")
    @Override
    public void caculateStockModification4Delete(List<Long> deleteList, List<StockFlowBillUpdateEntity> stockFlowBillUpdateList, Integer businessType, String corpid) throws XbbException {
        stockFlowBillHelp.caculateStockModification4Delete(deleteList, stockFlowBillUpdateList, businessType, corpid);

    }

    @Async(value = "importThreadPool")
    @Override
    public void insertMonthBeforeStockFlow(FlowBillInsertDTO flowBillInsertDTO) throws XbbException {
        Long beforeSeconds = flowBillInsertDTO.getBeforeSeconds();
        if (beforeSeconds == null) {
            beforeSeconds = BusinessConstant.THIRTY_ONE_DAY;
            flowBillInsertDTO.setBeforeSeconds(BusinessConstant.THIRTY_ONE_DAY);
        }
        String corpid = flowBillInsertDTO.getCorpid();
        Long todayInt = DateTimeUtil.getTodayInt();
        Long monthBeforeInt = todayInt - beforeSeconds;
        paasRedisHelper.setValue(RedisPrefixConstant.PRODUCE_STOCK_FLOW, corpid, new JSONObject(), RedisConstant.TINY_DURATION);
        try {
            // 插入当前分仓，往前推流水，后面会更新
            stockSurplusRecordModel.insertRecordsFromProductWarehouse(corpid, monthBeforeInt);
            Long idGte = 0L;
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put(StringConstant.CORPID, corpid);
            param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
            //获取所有仓库数据
            List<WarehouseEntityExt> warehouseEntityExtList = warehouseModel.findEntitys(param);
            if (warehouseEntityExtList.isEmpty()) {
                paasRedisHelper.removeValue(RedisPrefixConstant.PRODUCE_STOCK_FLOW, corpid);
                return;
            }
            //获取所有仓库id集合
            List<Long> warehouseIdList = new ArrayList<>();
            warehouseEntityExtList.forEach(item -> warehouseIdList.add(item.getId()));
            while (true) {
                //分页获取产品信息
                param.clear();
                param.put(StringConstant.CORPID, corpid);
                param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
                param.put("dataParentIdGte", 0L);
                param.put("idGte", idGte);
                param.put("pageSize", 1000);
                param.put("orderByStr", "id asc");
                List<ProductEntityExt> productEntityExtList = productModel.findEntitys(param);
                if (productEntityExtList.isEmpty()) {
                    break;
                }
                //每次查询出来最大的产品ID
                idGte = productEntityExtList.get(productEntityExtList.size() - 1).getId();
                //组装产品ID集合
                List<Long> productIdList = new ArrayList<>();
                productEntityExtList.forEach(item -> productIdList.add(item.getId()));
                List<StockFlowBillEntity> flowBillEntityList = new ArrayList<>();
                Map<String, Double> productWarehouseStockMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                Map<String, Double> productWarehouseCostMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                // 入库
                param.clear();
                param.put("corpid", corpid);
                param.put("productIdIn", productIdList);
                param.put("warehouseIdIn", warehouseIdList);
                param.put("del", DelEnum.NORMAL.getDel());
                //根据产品ID，仓库ID集合获取入库产品关联表数据
                List<InstockProductEntity> instockProductEntityList = instockProductModel.findEntitys(param);
                //入库单ID集合
                List<Long> idList = new ArrayList<>();
                //入库单map
                Map<Long, InstockEntityExt> instockEntityExtMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                if (!instockProductEntityList.isEmpty()) {
                    //入库单id 集合
                    List<Long> instockIdList = new ArrayList<>();
                    instockProductEntityList.forEach(item -> instockIdList.add(item.getIntoWarehouseId()));
                    //获取入库单集合
                    Map<String, Object> filterMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    filterMap.put(StringConstant.CORPID, corpid);
                    filterMap.put("idIn", instockIdList);
                    filterMap.put("del", 0);
                    filterMap.put("greaterThanDate", monthBeforeInt);
                    List<InstockEntityExt> instockEntityList = instockModel.findEntitys(filterMap);
                    instockEntityList.forEach(item -> {
                        idList.add(item.getId());
                        instockEntityExtMap.put(item.getId(), item);
                    });
                    for (InstockProductEntity entity : instockProductEntityList) {
                        //判断该入库产品关联数据对应的入库单ID 是否符合生成流水的时间范围
                        if (!idList.contains(entity.getIntoWarehouseId())) {
                            continue;
                        }
                        // 分仓总库存
                        String key = entity.getProductId() + "_" + entity.getWarehouseId();
                        Double productWarehouseStock = productWarehouseStockMap.getOrDefault(key, 0D);
                        productWarehouseStock = Arith.add(productWarehouseStock, entity.getProductNum());
                        productWarehouseStockMap.put(key, productWarehouseStock);
                        // 分仓总成本
                        Double productWarehouseCost = productWarehouseCostMap.getOrDefault(key, 0D);
                        productWarehouseCost = Arith.add(productWarehouseCost, Arith.mul(entity.getProductNum(), entity.getCost()));
                        productWarehouseCostMap.put(key, productWarehouseCost);
                        // 流水记录
                        //获取该产品入库关联的入库单
                        InstockEntityExt instockEntityExt = instockEntityExtMap.get(entity.getIntoWarehouseId());
                        Long dateTime = instockEntityExt.getData().getLong(InstockEnum.TIME.getAttr());
                        Integer type = instockEntityExt.getData().getInteger(InstockEnum.TYPE.getAttr());
                        Integer businessType = InstockTypeEnum.getStockBusinessTypeByCode(type) == 0 ? XbbRefTypeEnum.OTHER_INSTOCK.getCode() : InstockTypeEnum.getStockBusinessTypeByCode(type);
                        Long businessId = null;
                        String businessName = null;
                        if (Objects.equals(businessType, XbbRefTypeEnum.REFUND_INSTOCK.getCode())) {
                            businessId = instockEntityExt.getData().getLong(RefundInstockEnum.CUSTOMER_ID.getAttr());
                            businessName = instockEntityExt.getData().getString(RefundInstockEnum.CUSTOMER_ID_LINKED_TEXT.getAttr());
                        } else if (Objects.equals(businessType, XbbRefTypeEnum.PURCHASE_INSTOCK.getCode())) {
                            businessId = instockEntityExt.getData().getLong(PurchaseInstockEnum.SUPPLIER_ID.getAttr());
                            businessName = instockEntityExt.getData().getString(PurchaseInstockEnum.SUPPLIER_ID_LINKED_TEXT.getAttr());
                        }
                        StockFlowBillEntity stockFlowBillEntity = new StockFlowBillEntity(corpid, entity.getWarehouseId(), entity.getIntoWarehouseId(), entity.getId(), StockTypeEnum.IN_STOCK.getCode(), entity.getProductId(), instockEntityExt.getSerialNo(), businessId, businessName, entity.getProductNum(), entity.getCost(), null, null, dateTime, businessType, key, entity.getMemo());
                        flowBillEntityList.add(stockFlowBillEntity);
                    }
                }
                //出库
                //获取出库产品关联表数据
                List<OutstockProductEntity> outstockProductEntityList = outstockProductModel.findEntitys(param);
                idList.clear();
                Map<Long, OutstockEntityExt> outstockEntityExtMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                if (!outstockProductEntityList.isEmpty()) {
                    List<Long> outstockIdList = new ArrayList<>();
                    outstockProductEntityList.forEach(item -> outstockIdList.add(item.getOutWarehouseId()));
                    //获取出库单集合
                    Map<String, Object> filterMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    filterMap.put(StringConstant.CORPID, corpid);
                    filterMap.put("idIn", outstockIdList);
                    filterMap.put("del", 0);
                    filterMap.put("greaterThanDate", monthBeforeInt);
                    List<OutstockEntityExt> outstockEntityList = outstockModel.findEntitys(filterMap);
                    outstockEntityList.forEach(item -> {
                        idList.add(item.getId());
                        outstockEntityExtMap.put(item.getId(), item);
                    });
                    for (OutstockProductEntity entity : outstockProductEntityList) {
                        if (!idList.contains(entity.getOutWarehouseId())) {
                            continue;
                        }
                        // 分仓库存
                        String key = entity.getProductId() + "_" + entity.getWarehouseId();
                        Double productWarehouseStock = productWarehouseStockMap.getOrDefault(key, 0D);
                        productWarehouseStock = Arith.sub(productWarehouseStock, entity.getProductNum());
                        productWarehouseStockMap.put(key, productWarehouseStock);
                        // 分仓总成本
                        Double productWarehouseCost = productWarehouseCostMap.getOrDefault(key, 0D);
                        productWarehouseCost = Arith.sub(productWarehouseCost, Arith.mul(entity.getProductNum(), entity.getCost()));
                        productWarehouseCostMap.put(key, productWarehouseCost);
                        // 流水记录
                        OutstockEntityExt outstockEntityExt = outstockEntityExtMap.get(entity.getOutWarehouseId());
                        Long dateTime = outstockEntityExt.getData().getLong(OutstockEnum.TIME.getAttr());
                        Integer type = outstockEntityExt.getData().getInteger(OutstockEnum.TYPE.getAttr());
                        Integer businessType = OutstockTypeEnum.getStockBusinessTypeByCode(type) == 0 ? XbbRefTypeEnum.OTHER_OUTSTOCK.getCode() : OutstockTypeEnum.getStockBusinessTypeByCode(type);
                        Long businessId = null;
                        String businessName = null;
                        if (Objects.equals(businessType, XbbRefTypeEnum.CONTRACT_OUTSTOCK.getCode())) {
                            businessId = outstockEntityExt.getData().getLong(ContractOutstockEnum.LINK_CUSTOMER.getAttr());
                            businessName = outstockEntityExt.getData().getString(ContractOutstockEnum.LINK_CUSTOMER_HIDE.getAttr());
                        } else if (Objects.equals(businessType, XbbRefTypeEnum.RETURNED_PURCHASE_OUTSTOCK.getCode())) {
                            businessId = outstockEntityExt.getData().getLong(ReturnedPurchaseOutstockEnum.LINK_SUPPLIER.getAttr());
                            businessName = outstockEntityExt.getData().getString(ReturnedPurchaseOutstockEnum.LINK_SUPPLIER_HIDE.getAttr());
                        } else if (Objects.equals(businessType, XbbRefTypeEnum.WORK_ORDER_OUTSTOCK.getCode())) {
                            businessId = outstockEntityExt.getData().getLong(WorkOrderOutstockEnum.LINK_CUSTOMER.getAttr());
                            businessName = outstockEntityExt.getData().getString(WorkOrderOutstockEnum.LINK_CUSTOMER_HIDE.getAttr());
                        }
                        StockFlowBillEntity stockFlowBillEntity = new StockFlowBillEntity(corpid, entity.getWarehouseId(), entity.getOutWarehouseId(), entity.getId(), StockTypeEnum.OUT_STOCK.getCode(), entity.getProductId(), outstockEntityExt.getSerialNo(), businessId, businessName, entity.getProductNum(), entity.getCost(), null, null, dateTime, businessType, key, entity.getMemo());
                        flowBillEntityList.add(stockFlowBillEntity);
                    }
                }
                //成本调整单
                //获取成本调整单产品关联表数据
                List<CostAdjustProductEntity> costAdjustProductEntityList = costAdjustProductModel.findEntitys(param);
                idList.clear();
                Map<Long, CostAdjustEntityExt> costAdjustEntityExtMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                if (costAdjustProductEntityList != null && !costAdjustProductEntityList.isEmpty()) {
                    List<Long> costAdjustIdList = new ArrayList<>();
                    costAdjustProductEntityList.forEach(item -> costAdjustIdList.add(item.getCostAdjustId()));
                    //获取成本调整单集合
                    Map<String, Object> filterMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    filterMap.put(StringConstant.CORPID, corpid);
                    filterMap.put("idIn", costAdjustIdList);
                    filterMap.put("del", 0);
                    filterMap.put("greaterThanDate", monthBeforeInt);
                    List<CostAdjustEntityExt> costAdjustEntityExtList = costAdjustModel.findEntitys(filterMap);
                    costAdjustEntityExtList.forEach(item -> {
                        idList.add(item.getId());
                        costAdjustEntityExtMap.put(item.getId(), item);
                    });
                    for (CostAdjustProductEntity entity : costAdjustProductEntityList) {
                        if (!idList.contains(entity.getCostAdjustId())) {
                            continue;
                        }
                        // 分仓库存
                        String key = entity.getProductId() + "_" + entity.getWarehouseId();
                        // 分仓总成本
                        Double productWarehouseCost = productWarehouseCostMap.getOrDefault(key, 0D);
                        //成本调整单金额
                        productWarehouseCost = Arith.add(productWarehouseCost, entity.getCostAdjustAmount().doubleValue());
                        productWarehouseCostMap.put(key, productWarehouseCost);
                        // 流水记录
                        CostAdjustEntityExt costAdjustEntityExt = costAdjustEntityExtMap.get(entity.getCostAdjustId());
                        Long dateTime = costAdjustEntityExt.getData().getLong(CostAdjustEnum.ADJUST_TIME.getAttr());
                        Integer businessType = XbbRefTypeEnum.COST_ADJUST.getCode();
                        StockFlowBillEntity stockFlowBillEntity = new StockFlowBillEntity(corpid, entity.getWarehouseId(), entity.getCostAdjustId(), entity.getId(), StockTypeEnum.ADJUST_STOCK.getCode(), entity.getProductId(), costAdjustEntityExt.getSerialNo(), null, null, entity.getProductNum() == null ? 0 : entity.getProductNum(), entity.getCostAdjustAmount().doubleValue(), null, null, dateTime, businessType, key, entity.getMemo());
                        flowBillEntityList.add(stockFlowBillEntity);
                    }
                }
                param.clear();
                //获取期初数据
                param.put(StringConstant.CORPID, corpid);
                param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
                param.put("warehouseIdIn", warehouseIdList);
                param.put("greaterThanAddTime", monthBeforeInt);
                List<WarehouseOristockEntity> warehouseOristockEntityList = warehouseOristockModel.findEntitys(param);
                Map<Long, WarehouseOristockEntity> oriWarehouseMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                warehouseOristockEntityList.forEach(item -> oriWarehouseMap.put(item.getId().longValue(), item));
                param.put("oriStockIdIn", oriWarehouseMap.keySet());
                param.put("productIdIn", productIdList);
                //获取期初产品关联表数据
                List<WarehouseOristockProductEntity> warehouseOristockProductEntityList = warehouseOristockProductModel.findEntitys(param);
                for (WarehouseOristockProductEntity warehouseOristockProductEntity : warehouseOristockProductEntityList) {
                    if (oriWarehouseMap.containsKey(warehouseOristockProductEntity.getOristockId().longValue())) {
                        WarehouseOristockEntity warehouseOristockEntity = oriWarehouseMap.get(warehouseOristockProductEntity.getOristockId().longValue());
                        Long warehouseId = warehouseOristockEntity.getWarehouseId();
                        // 分仓库存
                        String key = warehouseOristockProductEntity.getProductId() + "_" + warehouseId;
                        Double productWarehouseStock = productWarehouseStockMap.getOrDefault(key, 0D);
                        productWarehouseStock = Arith.add(productWarehouseStock, warehouseOristockProductEntity.getOristockNum());
                        productWarehouseStockMap.put(key, productWarehouseStock);
                        // 分仓总成本
                        Double productWarehouseCost = productWarehouseCostMap.getOrDefault(key, 0D);
                        Double cost = warehouseOristockProductEntity.getCost() == null ? 0D : warehouseOristockProductEntity.getCost();
                        productWarehouseCost = Arith.add(productWarehouseCost, Arith.mul(warehouseOristockProductEntity.getOristockNum(), cost));
                        productWarehouseCostMap.put(key, productWarehouseCost);
                        // 流水记录
                        Long dateTime = DateTimeUtil.getTodayInt(warehouseOristockEntity.getAddTime());
                        StockFlowBillEntity stockFlowBillEntity = new StockFlowBillEntity(corpid, warehouseId, warehouseOristockProductEntity.getOristockId().longValue(), warehouseOristockProductEntity.getId(), StockTypeEnum.ORI_STOCK.getCode(), warehouseOristockProductEntity.getProductId(), warehouseOristockEntity.getSerialNo(), null, null, warehouseOristockProductEntity.getOristockNum(), cost, null, null, dateTime, XbbRefTypeEnum.ORI_STOCK.getCode(), key, warehouseOristockProductEntity.getMemo());
                        flowBillEntityList.add(stockFlowBillEntity);
                    }
                }
                //根据单据时间排序
                List<StockFlowBillEntity> sortedInsertList = flowBillEntityList.stream().sorted(Comparator.comparing(StockFlowBillEntity::getDate)).collect(Collectors.toList());
                Map<String, List<StockFlowBillEntity>> sortedInsertMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                sortedInsertList.forEach(item -> {
                    List<StockFlowBillEntity> sortedList = sortedInsertMap.getOrDefault(item.getWarehouseKey(), new ArrayList<>());
                    sortedList.add(item);
                    sortedInsertMap.put(item.getWarehouseKey(), sortedList);
                });
                if (!productWarehouseStockMap.keySet().isEmpty()) {
                    param.clear();
                    param.put(StringConstant.CORPID, corpid);
                    param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
                    param.put("date", monthBeforeInt);
                    param.put("warehouseKeyIn", productWarehouseStockMap.keySet());
                    List<StockSurplusRecordEntity> stockSurplusRecordList = stockSurplusRecordModel.findEntitys(param);
                    for (StockSurplusRecordEntity stockSurplusRecordEntity : stockSurplusRecordList) {
                        String warehouseKey = stockSurplusRecordEntity.getWarehouseKey();
                        if (sortedInsertMap.containsKey(warehouseKey)) {
                            Double remainCost = stockSurplusRecordEntity.getRemainingCost();
                            Double remainStock = stockSurplusRecordEntity.getRemainingStock();
                            List<StockFlowBillEntity> billEntityList = sortedInsertMap.get(warehouseKey);
                            sortedInsertMap.remove(warehouseKey);
                            for (int i = billEntityList.size() - 1; i >= 0; i--) {
                                StockFlowBillEntity stockFlowBillEntity = billEntityList.get(i);
                                if (i == billEntityList.size() - 1) {
                                    // 最后一次记录的结存等于当前结存
                                    stockFlowBillEntity.setRemainingCost(remainCost);
                                    stockFlowBillEntity.setRemainingStock(remainStock);
                                } else {
                                    // 前一次的结存等于后一次结存 - 后一次出入库数量和总成本
                                    StockFlowBillEntity lastStockFlowBillEntity = billEntityList.get(i + 1);
                                    Double num = lastStockFlowBillEntity.getNum();
                                    Double productCost = lastStockFlowBillEntity.getProductCost();
                                    Integer stockType = lastStockFlowBillEntity.getStockType();
                                    if (Objects.equals(stockType, StockTypeEnum.IN_STOCK.getCode()) || Objects.equals(stockType, StockTypeEnum.ORI_STOCK.getCode())) {
                                        remainStock = Arith.sub(remainStock, num);
                                        remainCost = Arith.sub(remainCost, Arith.mul(num, productCost));
                                        stockFlowBillEntity.setRemainingCost(remainCost);
                                        stockFlowBillEntity.setRemainingStock(remainStock);
                                    } else if (Objects.equals(stockType, StockTypeEnum.OUT_STOCK.getCode())) {
                                        remainStock = Arith.add(remainStock, num);
                                        remainCost = Arith.add(remainCost, Arith.mul(num, productCost));
                                        stockFlowBillEntity.setRemainingCost(remainCost);
                                        stockFlowBillEntity.setRemainingStock(remainStock);
                                    } else if (Objects.equals(stockType, StockTypeEnum.ADJUST_STOCK.getCode())) {
                                        remainCost = Arith.sub(remainCost, productCost);
                                        stockFlowBillEntity.setRemainingCost(remainCost);
                                        stockFlowBillEntity.setRemainingStock(remainStock);
                                    }
                                    if (stockFlowBillEntity.getRemainingCost() <= 0.000001 && stockFlowBillEntity.getRemainingCost() > -0.000001) {
                                        stockFlowBillEntity.setRemainingCost(0D);
                                        remainCost = 0D;
                                    }
                                    if (stockFlowBillEntity.getRemainingStock() <= 0.000001 && stockFlowBillEntity.getRemainingStock() > -0.000001) {
                                        stockFlowBillEntity.setRemainingStock(0D);
                                        remainStock = 0D;
                                    }
                                }
                            }
                        }
                        stockSurplusRecordEntity.setRemainingCost(stockSurplusRecordEntity.getRemainingCost() - productWarehouseCostMap.getOrDefault(warehouseKey, 0D));
                        if (stockSurplusRecordEntity.getRemainingCost() <= 0.000001 && stockSurplusRecordEntity.getRemainingCost() > -0.000001) {
                            stockSurplusRecordEntity.setRemainingCost(0D);
                        }
                        stockSurplusRecordEntity.setRemainingStock(stockSurplusRecordEntity.getRemainingStock() - productWarehouseStockMap.get(warehouseKey));
                        if (stockSurplusRecordEntity.getRemainingStock() <= 0.000001 && stockSurplusRecordEntity.getRemainingStock() > -0.000001) {
                            stockSurplusRecordEntity.setRemainingStock(0D);
                        }
                    }
                    // 解绑的流水也会生成
                    if (!sortedInsertMap.isEmpty()) {
                        for (List<StockFlowBillEntity> billEntityList : sortedInsertMap.values()) {
                            Double remainCost = 0D;
                            Double remainStock = 0D;
                            for (int i = billEntityList.size() - 1; i >= 0; i--) {
                                StockFlowBillEntity stockFlowBillEntity = billEntityList.get(i);
                                if (i == billEntityList.size() - 1) {
                                    // 最后一次记录的结存等于当前结存
                                    stockFlowBillEntity.setRemainingCost(remainCost);
                                    stockFlowBillEntity.setRemainingStock(remainStock);
                                } else {
                                    // 前一次的结存等于后一次结存 - 后一次出入库数量和总成本
                                    StockFlowBillEntity lastStockFlowBillEntity = billEntityList.get(i + 1);
                                    Double num = lastStockFlowBillEntity.getNum();
                                    Double productCost = lastStockFlowBillEntity.getProductCost();
                                    Integer stockType = lastStockFlowBillEntity.getStockType();
                                    if (Objects.equals(stockType, StockTypeEnum.IN_STOCK.getCode()) || Objects.equals(stockType, StockTypeEnum.ORI_STOCK.getCode())) {
                                        remainStock = Arith.sub(remainStock, num);
                                        remainCost = Arith.sub(remainCost, Arith.mul(num, productCost));
                                        stockFlowBillEntity.setRemainingCost(remainCost);
                                        stockFlowBillEntity.setRemainingStock(remainStock);
                                    } else if (Objects.equals(stockType, StockTypeEnum.OUT_STOCK.getCode())) {
                                        remainStock = Arith.add(remainStock, num);
                                        remainCost = Arith.add(remainCost, Arith.mul(num, productCost));
                                        stockFlowBillEntity.setRemainingCost(remainCost);
                                        stockFlowBillEntity.setRemainingStock(remainStock);
                                    } else if (Objects.equals(stockType, StockTypeEnum.ADJUST_STOCK.getCode())) {
                                        remainCost = Arith.sub(remainCost, productCost);
                                        stockFlowBillEntity.setRemainingCost(remainCost);
                                        stockFlowBillEntity.setRemainingStock(remainStock);
                                    }
                                    if (stockFlowBillEntity.getRemainingCost() <= 0.000001 && stockFlowBillEntity.getRemainingCost() > -0.000001) {
                                        stockFlowBillEntity.setRemainingCost(0D);
                                        remainCost = 0D;
                                    }
                                    if (stockFlowBillEntity.getRemainingStock() <= 0.000001 && stockFlowBillEntity.getRemainingStock() > -0.000001) {
                                        stockFlowBillEntity.setRemainingStock(0D);
                                        remainStock = 0D;
                                    }
                                }
                            }
                        }
                    }
                    if (!stockSurplusRecordList.isEmpty()) {
                        int pageSize = 1000;
                        int totalCount = sortedInsertList.size();
                        int start = 0;
                        while (true) {
                            if ((start + 1) * pageSize >= totalCount) {
                                stockFlowBillModel.insertBatch(sortedInsertList.subList(start * pageSize, totalCount));
                                break;
                            } else {
                                stockFlowBillModel.insertBatch(sortedInsertList.subList(start * pageSize, (start + 1) * pageSize));
                            }
                            start++;
                        }
                        stockSurplusRecordModel.updateBatch(stockSurplusRecordList, corpid);
                    }
                }
            }
            paasRedisHelper.removeValue(RedisPrefixConstant.PRODUCE_STOCK_FLOW, corpid);
        } catch (Exception e) {
            e.printStackTrace();
            LOG.warn("", e);
            //LOG.warn(e.getCause().getMessage());
            paasRedisHelper.removeValue(RedisPrefixConstant.PRODUCE_STOCK_FLOW, corpid);
        }
    }

    @Override
    public PaasMenuEntity addMenu(AccordConfigMenuPojo accordConfigMenuPojo) throws XbbException {
        String corpid = accordConfigMenuPojo.getCorpid();
        PaasMenuEntity stockFlowMenu = paasMenuModel.getByAlias(XbbRefTypeEnum.INVENTORY_MANAGEMENT.getAlias(), corpid);
        if (Objects.isNull(stockFlowMenu)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, I18nMessageUtil.getMessage(MessageConstant.MENU_NOT_EXIST));
        }
        Map<String, Object> params = new HashMap<>();
        params.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
        params.put("corpid", corpid);
        params.put("parentId", stockFlowMenu.getId());
        Integer count = paasMenuModel.getEntitysCount(params);
        long now = DateTimeUtil.getInt();
        List<JXCMenuEnum> jxcMenuEnums = Arrays.asList(JXCMenuEnum.STOCK_FLOW_BILL, JXCMenuEnum.STOCK_STANDING_BOOK);
        List<PaasMenuEntity> formAddMenuList = new ArrayList<>();
        for (JXCMenuEnum jxcMenuEnum : jxcMenuEnums) {
            PaasMenuEntity paasMenuEntity = new PaasMenuEntity();
            paasMenuEntity.setCorpid(stockFlowMenu.getCorpid());
            paasMenuEntity.setAppId(stockFlowMenu.getAppId());
            paasMenuEntity.setParentId(stockFlowMenu.getId());
            paasMenuEntity.setSort(++count);
            paasMenuEntity.setCreatorId(accordConfigMenuPojo.getUserId());
            paasMenuEntity.setAlias(jxcMenuEnum.getAlias());
            paasMenuEntity.setColor(jxcMenuEnum.getAccessLinkEnum().getColor());
            paasMenuEntity.setIcon(jxcMenuEnum.getAccessLinkEnum().getIcon());
            paasMenuEntity.setSaasMark(1);
            paasMenuEntity.setUrl(jxcMenuEnum.getAccessLinkEnum().getUrl());
            paasMenuEntity.setMobileUrl(jxcMenuEnum.getAccessLinkEnum().getMobileUrl());
            paasMenuEntity.setType(MenuTypeEnum.FORM.getType());
            paasMenuEntity.setEnable(1);
            paasMenuEntity.setName(jxcMenuEnum.getName());
            paasMenuEntity.setCreatorId(accordConfigMenuPojo.getUserId());
            paasMenuEntity.setAddTime(now);
            paasMenuEntity.setUpdateTime(now);
            //新增经销商标志
            paasMenuEntity.setDistributorMark(BasicConstant.ZERO);
            formAddMenuList.add(paasMenuEntity);
        }
        List<PaasMenuEntity> formMenuList = paasMenuModel.insertBatch(formAddMenuList);
        // 插入菜单对应的表单
        FormAddBatchDTO formAddBatchDTO = new FormAddBatchDTO();
        BeanUtil.copyProperties(accordConfigMenuPojo, formAddBatchDTO);
        List<FormEntityAddDTO> formList = new ArrayList<>();
        initJxcMenuHelp.getFormList(formList, formMenuList);
        formAddBatchDTO.setFormList(formList);
        FormAddBatchVO formAddBatchVO = paasFormService.addBatch(formAddBatchDTO);
        if (Objects.isNull(formAddBatchVO)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        MenuInitDTO menuInitDTO = new MenuInitDTO();
        BeanUtil.copyProperties(accordConfigMenuPojo, menuInitDTO);
        // 插入表单对应的字段解释
        initJxcMenuHelp.formExplainAddBatch(menuInitDTO, formAddBatchVO);
        PaasMenuEntity batchFlowMenuEntity = paasMenuModel.getByAlias(XbbRefTypeEnum.BATCH_FLOW_BILL.getAlias(), accordConfigMenuPojo.getCorpid());
        if (Objects.nonNull(batchFlowMenuEntity)) {
            CompanyConfigEntity companyConfigEntity = companyConfigModel.getByConfigAlias(CompanyConfigEnum.ENABLE_BATCH_SHELF_MANAGEMENT.getAlias(), accordConfigMenuPojo.getCorpid());
            if (Objects.nonNull(companyConfigEntity) && Objects.equals(BasicConstant.IS_USE.toString(), companyConfigEntity.getConfigValue())) {
                batchFlowMenuEntity.setEnable(1);
            } else {
                batchFlowMenuEntity.setEnable(0);
            }
            paasMenuModel.update(batchFlowMenuEntity);
        }
        return null;
    }

    @Override
    public void insertBeforeStockFlow(FlowBillInsertDTO flowBillInsertDTO) throws XbbException {
        Long beforeSeconds = flowBillInsertDTO.getBeforeSeconds();
        if (beforeSeconds == null) {
            beforeSeconds = BusinessConstant.THIRTY_ONE_DAY;
            flowBillInsertDTO.setBeforeSeconds(BusinessConstant.THIRTY_ONE_DAY);
        }
        String corpid = flowBillInsertDTO.getCorpid();
        Long todayInt = DateTimeUtil.getTodayInt();
        Long monthBeforeInt = todayInt - beforeSeconds;
        try {
            Long idGte = 0L;
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put(StringConstant.CORPID, corpid);
            param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
            //获取所有仓库数据
            List<WarehouseEntityExt> warehouseEntityExtList = warehouseModel.findEntitys(param);
            if (warehouseEntityExtList.isEmpty()) {
                paasRedisHelper.removeValue(RedisPrefixConstant.PRODUCE_STOCK_FLOW, corpid);
                return;
            }
            //获取所有仓库id集合
            List<Long> warehouseIdList = new ArrayList<>();
            warehouseEntityExtList.forEach(item -> warehouseIdList.add(item.getId()));
            while (true) {
                //分页获取产品信息
                param.clear();
                param.put(StringConstant.CORPID, corpid);
                param.put("columns", new StringBuffer(" id "));
                param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
                param.put("dataParentIdGte", 0L);
                param.put("idGte", idGte);
                param.put("pageSize", 1000);
                param.put("orderByStr", "id asc");
                List<ProductEntityExt> productEntityExtList = productModel.findEntitysWithoutSub(param);
                if (productEntityExtList.isEmpty()) {
                    break;
                }
                //每次查询出来最大的产品ID
                idGte = productEntityExtList.get(productEntityExtList.size() - 1).getId();
                //组装产品ID集合
                List<Long> productIdList = new ArrayList<>();
                productEntityExtList.forEach(item -> productIdList.add(item.getId()));
                List<StockFlowBillEntity> flowBillEntityList = new ArrayList<>();
                Map<String, Double> productWarehouseStockMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                Map<String, Double> productWarehouseCostMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                // 入库
                param.clear();
                param.put("corpid", corpid);
                param.put("productIdIn", productIdList);
                param.put("warehouseIdIn", warehouseIdList);
                param.put("del", DelEnum.NORMAL.getDel());
                //根据产品ID，仓库ID集合获取入库产品关联表数据
                List<InstockProductEntity> instockProductEntityList = instockProductModel.findEntitys(param);
                //入库单ID集合
                List<Long> idList = new ArrayList<>();
                //入库单map
                Map<Long, InstockEntityExt> instockEntityExtMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                if (!instockProductEntityList.isEmpty()) {
                    //入库单id 集合
                    List<Long> instockIdList = new ArrayList<>();
                    instockProductEntityList.forEach(item -> instockIdList.add(item.getIntoWarehouseId()));
                    //获取入库单集合
                    Map<String, Object> filterMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    filterMap.put(StringConstant.CORPID, corpid);
                    filterMap.put("idIn", instockIdList);
                    filterMap.put("del", 0);
                    filterMap.put("greaterThanDate", monthBeforeInt);
                    List<InstockEntityExt> instockEntityList = instockModel.findEntitys(filterMap);
                    instockEntityList.forEach(item -> {
                        idList.add(item.getId());
                        instockEntityExtMap.put(item.getId(), item);
                    });
                    //去除工作流中数据
                    removeAddData(idList, corpid);
                    for (InstockProductEntity entity : instockProductEntityList) {
                        //判断该入库产品关联数据对应的入库单ID 是否符合生成流水的时间范围
                        if (!idList.contains(entity.getIntoWarehouseId())) {
                            continue;
                        }
                        // 分仓总库存
                        String key = entity.getProductId() + "_" + entity.getWarehouseId();
                        Double productWarehouseStock = productWarehouseStockMap.getOrDefault(key, 0D);
                        productWarehouseStock = Arith.add(productWarehouseStock, entity.getProductNum());
                        productWarehouseStockMap.put(key, productWarehouseStock);
                        // 分仓总成本
                        Double productWarehouseCost = productWarehouseCostMap.getOrDefault(key, 0D);
                        productWarehouseCost = Arith.add(productWarehouseCost, Arith.mul(entity.getProductNum(), entity.getCost()));
                        productWarehouseCostMap.put(key, productWarehouseCost);
                        // 流水记录
                        //获取该产品入库关联的入库单
                        InstockEntityExt instockEntityExt = instockEntityExtMap.get(entity.getIntoWarehouseId());
                        Long dateTime = instockEntityExt.getData().getLong(InstockEnum.TIME.getAttr());
                        Integer type = instockEntityExt.getData().getInteger(InstockEnum.TYPE.getAttr());
                        Integer businessType = InstockTypeEnum.getStockBusinessTypeByCode(type) == 0 ? XbbRefTypeEnum.OTHER_INSTOCK.getCode() : InstockTypeEnum.getStockBusinessTypeByCode(type);
                        Long businessId = null;
                        String businessName = null;
                        if (Objects.equals(businessType, XbbRefTypeEnum.REFUND_INSTOCK.getCode())) {
                            businessId = instockEntityExt.getData().getLong(RefundInstockEnum.CUSTOMER_ID.getAttr());
                            businessName = instockEntityExt.getData().getString(RefundInstockEnum.CUSTOMER_ID_LINKED_TEXT.getAttr());
                        } else if (Objects.equals(businessType, XbbRefTypeEnum.PURCHASE_INSTOCK.getCode())) {
                            businessId = instockEntityExt.getData().getLong(PurchaseInstockEnum.SUPPLIER_ID.getAttr());
                            businessName = instockEntityExt.getData().getString(PurchaseInstockEnum.SUPPLIER_ID_LINKED_TEXT.getAttr());
                        }
                        businessName = StringUtil.filterEmoji(businessName);
                        StockFlowBillEntity stockFlowBillEntity = new StockFlowBillEntity(corpid, entity.getWarehouseId(), entity.getIntoWarehouseId(), entity.getId(), StockTypeEnum.IN_STOCK.getCode(), entity.getProductId(), instockEntityExt.getSerialNo(), businessId, businessName, entity.getProductNum(), entity.getCost(), null, null, dateTime, businessType, key, entity.getMemo(),entity.getAddTime());
                        flowBillEntityList.add(stockFlowBillEntity);
                    }
                }
                //出库
                //获取出库产品关联表数据
                List<OutstockProductEntity> outstockProductEntityList = outstockProductModel.findEntitys(param);
                idList.clear();
                Map<Long, OutstockEntityExt> outstockEntityExtMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                if (!outstockProductEntityList.isEmpty()) {
                    List<Long> outstockIdList = new ArrayList<>();
                    outstockProductEntityList.forEach(item -> outstockIdList.add(item.getOutWarehouseId()));
                    //获取出库单集合
                    Map<String, Object> filterMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    filterMap.put(StringConstant.CORPID, corpid);
                    filterMap.put("idIn", outstockIdList);
                    filterMap.put("del", 0);
                    filterMap.put("greaterThanDate", monthBeforeInt);
                    List<OutstockEntityExt> outstockEntityList = outstockModel.findEntitys(filterMap);
                    outstockEntityList.forEach(item -> {
                        idList.add(item.getId());
                        outstockEntityExtMap.put(item.getId(), item);
                    });
                    //去除工作流中数据
                    removeAddData(idList, corpid);
                    for (OutstockProductEntity entity : outstockProductEntityList) {
                        if (!idList.contains(entity.getOutWarehouseId())) {
                            continue;
                        }
                        // 分仓库存
                        String key = entity.getProductId() + "_" + entity.getWarehouseId();
                        Double productWarehouseStock = productWarehouseStockMap.getOrDefault(key, 0D);
                        productWarehouseStock = Arith.sub(productWarehouseStock, entity.getProductNum());
                        productWarehouseStockMap.put(key, productWarehouseStock);
                        // 分仓总成本
                        Double productWarehouseCost = productWarehouseCostMap.getOrDefault(key, 0D);
                        productWarehouseCost = Arith.sub(productWarehouseCost, Arith.mul(entity.getProductNum(), entity.getCost()));
                        productWarehouseCostMap.put(key, productWarehouseCost);
                        // 流水记录
                        OutstockEntityExt outstockEntityExt = outstockEntityExtMap.get(entity.getOutWarehouseId());
                        Long dateTime = outstockEntityExt.getData().getLong(OutstockEnum.TIME.getAttr());
                        Integer type = outstockEntityExt.getData().getInteger(OutstockEnum.TYPE.getAttr());
                        Integer businessType = OutstockTypeEnum.getStockBusinessTypeByCode(type) == 0 ? XbbRefTypeEnum.OTHER_OUTSTOCK.getCode() : OutstockTypeEnum.getStockBusinessTypeByCode(type);
                        Long businessId = null;
                        String businessName = null;
                        if (Objects.equals(businessType, XbbRefTypeEnum.CONTRACT_OUTSTOCK.getCode())) {
                            businessId = outstockEntityExt.getData().getLong(ContractOutstockEnum.LINK_CUSTOMER.getAttr());
                            businessName = outstockEntityExt.getData().getString(ContractOutstockEnum.LINK_CUSTOMER_HIDE.getAttr());
                        } else if (Objects.equals(businessType, XbbRefTypeEnum.RETURNED_PURCHASE_OUTSTOCK.getCode())) {
                            businessId = outstockEntityExt.getData().getLong(ReturnedPurchaseOutstockEnum.LINK_SUPPLIER.getAttr());
                            businessName = outstockEntityExt.getData().getString(ReturnedPurchaseOutstockEnum.LINK_SUPPLIER_HIDE.getAttr());
                        } else if (Objects.equals(businessType, XbbRefTypeEnum.WORK_ORDER_OUTSTOCK.getCode())) {
                            businessId = outstockEntityExt.getData().getLong(WorkOrderOutstockEnum.LINK_CUSTOMER.getAttr());
                            businessName = outstockEntityExt.getData().getString(WorkOrderOutstockEnum.LINK_CUSTOMER_HIDE.getAttr());
                        }
                        businessName = StringUtil.filterEmoji(businessName);
                        StockFlowBillEntity stockFlowBillEntity = new StockFlowBillEntity(corpid, entity.getWarehouseId(), entity.getOutWarehouseId(), entity.getId(), StockTypeEnum.OUT_STOCK.getCode(), entity.getProductId(), outstockEntityExt.getSerialNo(), businessId, businessName, entity.getProductNum(), entity.getCost(), null, null, dateTime, businessType, key, entity.getMemo(), entity.getAddTime());
                        flowBillEntityList.add(stockFlowBillEntity);
                    }
                }
                //成本调整单
                //获取成本调整单产品关联表数据
                List<CostAdjustProductEntity> costAdjustProductEntityList = costAdjustProductModel.findEntitys(param);
                idList.clear();
                Map<Long, CostAdjustEntityExt> costAdjustEntityExtMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                if (costAdjustProductEntityList != null && !costAdjustProductEntityList.isEmpty()) {
                    List<Long> costAdjustIdList = new ArrayList<>();
                    costAdjustProductEntityList.forEach(item -> costAdjustIdList.add(item.getCostAdjustId()));
                    //获取成本调整单集合
                    Map<String, Object> filterMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    filterMap.put(StringConstant.CORPID, corpid);
                    filterMap.put("idIn", costAdjustIdList);
                    filterMap.put("del", 0);
                    filterMap.put("greaterThanDate", monthBeforeInt);
                    List<CostAdjustEntityExt> costAdjustEntityExtList = costAdjustModel.findEntitys(filterMap);
                    costAdjustEntityExtList.forEach(item -> {
                        idList.add(item.getId());
                        costAdjustEntityExtMap.put(item.getId(), item);
                    });
                    for (CostAdjustProductEntity entity : costAdjustProductEntityList) {
                        if (!idList.contains(entity.getCostAdjustId())) {
                            continue;
                        }
                        // 分仓库存
                        String key = entity.getProductId() + "_" + entity.getWarehouseId();
                        // 分仓总成本
                        Double productWarehouseCost = productWarehouseCostMap.getOrDefault(key, 0D);
                        //成本调整单金额
                        productWarehouseCost = Arith.add(productWarehouseCost, entity.getCostAdjustAmount().doubleValue());
                        productWarehouseCostMap.put(key, productWarehouseCost);
                        // 流水记录
                        CostAdjustEntityExt costAdjustEntityExt = costAdjustEntityExtMap.get(entity.getCostAdjustId());
                        Long dateTime = costAdjustEntityExt.getData().getLong(CostAdjustEnum.ADJUST_TIME.getAttr());
                        Integer businessType = XbbRefTypeEnum.COST_ADJUST.getCode();
                        StockFlowBillEntity stockFlowBillEntity = new StockFlowBillEntity(corpid, entity.getWarehouseId(), entity.getCostAdjustId(), entity.getId(), StockTypeEnum.ADJUST_STOCK.getCode(), entity.getProductId(), costAdjustEntityExt.getSerialNo(), null, null, entity.getProductNum() == null ? 0 : entity.getProductNum(), entity.getCostAdjustAmount().doubleValue(), null, null, dateTime, businessType, key, entity.getMemo(), Long.valueOf(entity.getAddTime()));
                        flowBillEntityList.add(stockFlowBillEntity);
                    }
                }
                param.clear();
                //获取期初数据
                param.put(StringConstant.CORPID, corpid);
                param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
                param.put("warehouseIdIn", warehouseIdList);
                param.put("greaterThanAddTime", monthBeforeInt);
                List<WarehouseOristockEntity> warehouseOristockEntityList = warehouseOristockModel.findEntitys(param);
                Map<Long, WarehouseOristockEntity> oriWarehouseMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                warehouseOristockEntityList.forEach(item -> oriWarehouseMap.put(item.getId().longValue(), item));
                param.put("oriStockIdIn", oriWarehouseMap.keySet());
                param.put("productIdIn", productIdList);
                //获取期初产品关联表数据
                List<WarehouseOristockProductEntity> warehouseOristockProductEntityList = warehouseOristockProductModel.findEntitys(param);
                for (WarehouseOristockProductEntity warehouseOristockProductEntity : warehouseOristockProductEntityList) {
                    if (oriWarehouseMap.containsKey(warehouseOristockProductEntity.getOristockId().longValue())) {
                        WarehouseOristockEntity warehouseOristockEntity = oriWarehouseMap.get(warehouseOristockProductEntity.getOristockId().longValue());
                        Long warehouseId = warehouseOristockEntity.getWarehouseId();
                        // 分仓库存
                        String key = warehouseOristockProductEntity.getProductId() + "_" + warehouseId;
                        Double productWarehouseStock = productWarehouseStockMap.getOrDefault(key, 0D);
                        productWarehouseStock = Arith.add(productWarehouseStock, warehouseOristockProductEntity.getOristockNum());
                        productWarehouseStockMap.put(key, productWarehouseStock);
                        // 分仓总成本
                        Double productWarehouseCost = productWarehouseCostMap.getOrDefault(key, 0D);
                        Double cost = warehouseOristockProductEntity.getCost() == null ? 0D : warehouseOristockProductEntity.getCost();
                        productWarehouseCost = Arith.add(productWarehouseCost, Arith.mul(warehouseOristockProductEntity.getOristockNum(), cost));
                        productWarehouseCostMap.put(key, productWarehouseCost);
                        // 流水记录
                        Long dateTime = DateTimeUtil.getTodayInt(warehouseOristockEntity.getAddTime());
                        StockFlowBillEntity stockFlowBillEntity = new StockFlowBillEntity(corpid, warehouseId, warehouseOristockProductEntity.getOristockId().longValue(), warehouseOristockProductEntity.getId(), StockTypeEnum.ORI_STOCK.getCode(), warehouseOristockProductEntity.getProductId(), warehouseOristockEntity.getSerialNo(), null, null, warehouseOristockProductEntity.getOristockNum(), cost, null, null, dateTime, XbbRefTypeEnum.ORI_STOCK.getCode(), key, warehouseOristockProductEntity.getMemo(), warehouseOristockProductEntity.getAddTime());
                        flowBillEntityList.add(stockFlowBillEntity);
                    }
                }
                //根据单据时间排序
                List<StockFlowBillEntity> sortedInsertList = flowBillEntityList.stream().sorted(Comparator.comparing(StockFlowBillEntity::getDate)).collect(Collectors.toList());
                Map<String, List<StockFlowBillEntity>> sortedInsertMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                sortedInsertList.forEach(item -> {
                    List<StockFlowBillEntity> sortedList = sortedInsertMap.getOrDefault(item.getWarehouseKey(), new ArrayList<>());
                    sortedList.add(item);
                    sortedInsertMap.put(item.getWarehouseKey(), sortedList);
                });
                if (!productWarehouseStockMap.keySet().isEmpty()) {
                    param.clear();
                    param.put(StringConstant.CORPID, corpid);
                    param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
                    param.put("date", monthBeforeInt);
                    param.put("warehouseKeyIn", productWarehouseStockMap.keySet());
                    List<StockSurplusRecordEntity> stockSurplusRecordList = stockSurplusRecordModel.findEntitys(param);
                    for (StockSurplusRecordEntity stockSurplusRecordEntity : stockSurplusRecordList) {
                        String warehouseKey = stockSurplusRecordEntity.getWarehouseKey();
                        if (sortedInsertMap.containsKey(warehouseKey)) {
                            Double remainCost = stockSurplusRecordEntity.getRemainingCost();
                            Double remainStock = stockSurplusRecordEntity.getRemainingStock();
                            List<StockFlowBillEntity> billEntityList = sortedInsertMap.get(warehouseKey);
                            sortedInsertMap.remove(warehouseKey);
                            for (int i = billEntityList.size() - 1; i >= 0; i--) {
                                StockFlowBillEntity stockFlowBillEntity = billEntityList.get(i);
                                if (i == billEntityList.size() - 1) {
                                    // 最后一次记录的结存等于当前结存
                                    stockFlowBillEntity.setRemainingCost(remainCost);
                                    stockFlowBillEntity.setRemainingStock(remainStock);
                                } else {
                                    // 前一次的结存等于后一次结存 - 后一次出入库数量和总成本
                                    StockFlowBillEntity lastStockFlowBillEntity = billEntityList.get(i + 1);
                                    Double num = lastStockFlowBillEntity.getNum();
                                    Double productCost = lastStockFlowBillEntity.getProductCost();
                                    Integer stockType = lastStockFlowBillEntity.getStockType();
                                    if (Objects.equals(stockType, StockTypeEnum.IN_STOCK.getCode()) || Objects.equals(stockType, StockTypeEnum.ORI_STOCK.getCode())) {
                                        remainStock = Arith.sub(remainStock, num);
                                        remainCost = Arith.sub(remainCost, Arith.mul(num, productCost));
                                        stockFlowBillEntity.setRemainingCost(remainCost);
                                        stockFlowBillEntity.setRemainingStock(remainStock);
                                    } else if (Objects.equals(stockType, StockTypeEnum.OUT_STOCK.getCode())) {
                                        remainStock = Arith.add(remainStock, num);
                                        remainCost = Arith.add(remainCost, Arith.mul(num, productCost));
                                        stockFlowBillEntity.setRemainingCost(remainCost);
                                        stockFlowBillEntity.setRemainingStock(remainStock);
                                    } else if (Objects.equals(stockType, StockTypeEnum.ADJUST_STOCK.getCode())) {
                                        remainCost = Arith.sub(remainCost, productCost);
                                        stockFlowBillEntity.setRemainingCost(remainCost);
                                        stockFlowBillEntity.setRemainingStock(remainStock);
                                    }
                                    if (stockFlowBillEntity.getRemainingCost() <= 0.000001 && stockFlowBillEntity.getRemainingCost() > -0.000001) {
                                        stockFlowBillEntity.setRemainingCost(0D);
                                        remainCost = 0D;
                                    }
                                    if (stockFlowBillEntity.getRemainingStock() <= 0.000001 && stockFlowBillEntity.getRemainingStock() > -0.000001) {
                                        stockFlowBillEntity.setRemainingStock(0D);
                                        remainStock = 0D;
                                    }
                                }
                            }
                        }
                        stockSurplusRecordEntity.setRemainingCost(stockSurplusRecordEntity.getRemainingCost() - productWarehouseCostMap.getOrDefault(warehouseKey, 0D));
                        if (stockSurplusRecordEntity.getRemainingCost() <= 0.000001 && stockSurplusRecordEntity.getRemainingCost() > -0.000001) {
                            stockSurplusRecordEntity.setRemainingCost(0D);
                        }
                        stockSurplusRecordEntity.setRemainingStock(stockSurplusRecordEntity.getRemainingStock() - productWarehouseStockMap.get(warehouseKey));
                        if (stockSurplusRecordEntity.getRemainingStock() <= 0.000001 && stockSurplusRecordEntity.getRemainingStock() > -0.000001) {
                            stockSurplusRecordEntity.setRemainingStock(0D);
                        }
                    }
                    // 解绑的流水也会生成
                    if (!sortedInsertMap.isEmpty()) {
                        for (List<StockFlowBillEntity> billEntityList : sortedInsertMap.values()) {
                            Double remainCost = 0D;
                            Double remainStock = 0D;
                            for (int i = billEntityList.size() - 1; i >= 0; i--) {
                                StockFlowBillEntity stockFlowBillEntity = billEntityList.get(i);
                                if (i == billEntityList.size() - 1) {
                                    // 最后一次记录的结存等于当前结存
                                    stockFlowBillEntity.setRemainingCost(remainCost);
                                    stockFlowBillEntity.setRemainingStock(remainStock);
                                } else {
                                    // 前一次的结存等于后一次结存 - 后一次出入库数量和总成本
                                    StockFlowBillEntity lastStockFlowBillEntity = billEntityList.get(i + 1);
                                    Double num = lastStockFlowBillEntity.getNum();
                                    Double productCost = lastStockFlowBillEntity.getProductCost();
                                    Integer stockType = lastStockFlowBillEntity.getStockType();
                                    if (Objects.equals(stockType, StockTypeEnum.IN_STOCK.getCode()) || Objects.equals(stockType, StockTypeEnum.ORI_STOCK.getCode())) {
                                        remainStock = Arith.sub(remainStock, num);
                                        remainCost = Arith.sub(remainCost, Arith.mul(num, productCost));
                                        stockFlowBillEntity.setRemainingCost(remainCost);
                                        stockFlowBillEntity.setRemainingStock(remainStock);
                                    } else if (Objects.equals(stockType, StockTypeEnum.OUT_STOCK.getCode())) {
                                        remainStock = Arith.add(remainStock, num);
                                        remainCost = Arith.add(remainCost, Arith.mul(num, productCost));
                                        stockFlowBillEntity.setRemainingCost(remainCost);
                                        stockFlowBillEntity.setRemainingStock(remainStock);
                                    } else if (Objects.equals(stockType, StockTypeEnum.ADJUST_STOCK.getCode())) {
                                        remainCost = Arith.sub(remainCost, productCost);
                                        stockFlowBillEntity.setRemainingCost(remainCost);
                                        stockFlowBillEntity.setRemainingStock(remainStock);
                                    }
                                    if (stockFlowBillEntity.getRemainingCost() <= 0.000001 && stockFlowBillEntity.getRemainingCost() > -0.000001) {
                                        stockFlowBillEntity.setRemainingCost(0D);
                                        remainCost = 0D;
                                    }
                                    if (stockFlowBillEntity.getRemainingStock() <= 0.000001 && stockFlowBillEntity.getRemainingStock() > -0.000001) {
                                        stockFlowBillEntity.setRemainingStock(0D);
                                        remainStock = 0D;
                                    }
                                }
                            }
                        }
                    }
                    if (!stockSurplusRecordList.isEmpty()) {
                        int pageSize = 1000;
                        int totalCount = sortedInsertList.size();
                        int start = 0;
                        while (true) {
                            if ((start + 1) * pageSize >= totalCount) {
                                stockFlowBillModel.insertBatch(sortedInsertList.subList(start * pageSize, totalCount));
                                break;
                            } else {
                                stockFlowBillModel.insertBatch(sortedInsertList.subList(start * pageSize, (start + 1) * pageSize));
                            }
                            start++;
                        }
                        stockSurplusRecordModel.updateBatch(stockSurplusRecordList, corpid);
                    }
                }
            }
        } catch (Exception e) {
            LOG.warn("", e);
            //LOG.warn(e.getCause().getMessage());
            LOG.error("公司" + corpid + "失败了" + e);
        }
    }




    /**
     * 去掉新建进流程的数据
     * @param idList
     * @param corpid
     */
    private void removeAddData(List<Long> idList, String corpid) {
        if (CollectionsUtil.isNotEmpty(idList)) {
            if (!commonHelp.isOpenWorkFlow(corpid)) {
                // 检测是否开通了工作流
                return;
            }
            // 查询审批中的任务
            Map<String, Object> filterMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            filterMap.put(StringConstant.CORPID, corpid);
            filterMap.put("dataIdIn", idList);
            filterMap.put("del", 0);
            filterMap.put("flowStatusIn", FlowStatusEnum.inApproval());
            filterMap.put("columns", " id, data_id, flow_status");
            List<WorkflowTaskEntity> taskList = workflowTaskModel.findEntities(filterMap);
            if (CollectionsUtil.isNotEmpty(taskList)) {
                List<Long> taskIdList = new ArrayList<>();
                taskList.forEach(item->{
                    taskIdList.add(item.getId());
                });
                filterMap.clear();
                filterMap.put(StringConstant.CORPID, corpid);
                filterMap.put("taskIdIn", taskIdList);
                filterMap.put("del", 0);
                List<WorkflowTransferDataEntity> list = workflowTransferDataModel.findEntities(filterMap);
                if (CollectionsUtil.isNotEmpty(list)) {
                    WorkflowTransferPOJO workflowTransferPOJO;
                    for (WorkflowTransferDataEntity item : list) {
                        workflowTransferPOJO = JSON.parseObject(item.getData().toJSONString(), WorkflowTransferPOJO.class);
                        Integer operate = workflowTransferPOJO.getOperate();
                        if (Objects.equals(WorkflowOperateEnum.ADD.getType(), operate)) {
//                            移除掉新建的入库单或者出库单
                            // 工作流的新建时，入库产品表和出库产品表都会生成；但是库存并未增加，所以需要移除掉新建进工作流的入库单和出库单
                            idList.remove(workflowTransferPOJO.getData().getId());
                        }
                    }
                }
            }
        }
    }
}
